package com.redis.bloom.filter.utils;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.redis.bloom.filter.config.RedissonInitConfiguration;
import com.redis.bloom.filter.enums.BloomFilterEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

/**
 * 布隆过滤器工具类
 *
 * @author chen
 * @date 2024年03月03日 10:44
 */
@Component
@Slf4j
@DependsOn("redissonInitConfiguration")
public class BloomFilterUtils{
    private static final String RESULT_ADD_FAIL = "添加失败";
    private static final String VALUE_EXIST = "value值已存在";


    @PostConstruct
    public void init(){
        //布隆过滤器枚举，生产环境应该做成配置化
        BloomFilterEnum[] bloomFilterValues = BloomFilterEnum.values();
        for(BloomFilterEnum filterEnum : bloomFilterValues) {
            //初始化布隆过滤器
            initBloomFilter(filterEnum);
        }
    }

    /**
     * @author chen
     * 增加值到布隆过滤器操作：必需通过基础校验才返回true,否则返回false
     * @param bloomFilterName 布隆过滤器名称
     * @param value 值
     * @date: 2024/2/27 8:29
     * @return boolean 是否添加成功
     */
    public static <V> boolean addValueToBloom(String bloomFilterName, V value) {
        long start = System.currentTimeMillis();
        if(StringUtils.isBlank(bloomFilterName) || value == null) {
            return false;
        }
        try {
            RBloomFilter<V> bloomFilter = getRedissonClient().getBloomFilter(bloomFilterName);
            boolean addResult = bloomFilter.add(value);
            log.info("布隆过滤器添加值#bloomFilterName={}#value={}#耗时={}ms#result={}",bloomFilterName,value,(System.currentTimeMillis() - start),addResult);
            if(!addResult) {
                boolean isContains = bloomFilter.contains(value);
                if(isContains) {
                    log.info("布隆过滤器添加值#bloomFilterName={}#value={}#耗时={}ms#result={}",bloomFilterName,value,(System.currentTimeMillis() - start), VALUE_EXIST);
                    return true;
                }
                log.error("布隆过滤器添加值#bloomFilterName={}#value={}#耗时={}ms#result={}", bloomFilterName,value,(System.currentTimeMillis() - start),RESULT_ADD_FAIL);
                return false;
            }
            return true;
        } catch (Exception e) {
            log.error("布隆过滤器添加值异常#bloomFilterName={}#value={}#耗时={}ms",bloomFilterName,value,(System.currentTimeMillis() - start), e);
            return false;
        }
    }

    /**
     * @author chen
     * 布隆过滤器校验值是否存在，如果为false,则代表一定不存在, 如果true则代表可能存在，需要重新查询数据库确认是否存在
     * @param bloomFilterName
     * @param value
     * @date: 2024/2/27 8:37
     * @return boolean true-存在 false-不存在
     */
    public static <V> boolean isContainsInBloom(String bloomFilterName, V value) {
        long begin = System.currentTimeMillis();
        if(StringUtils.isBlank(bloomFilterName) || value == null) {
            return false;
        }
        try {
            RBloomFilter<V> bloomFilter = getRedissonClient().getBloomFilter(bloomFilterName);
            boolean isContains = bloomFilter.contains(value);
            log.info("布隆过滤器校验值是否存在操作isContains:{},耗时:{}ms,bloomFilterName:{},value:{}",
                    isContains, (System.currentTimeMillis() - begin), bloomFilterName,value);
            return isContains;
        } catch (Exception e) {
            log.error("布隆过滤器校验出异常,默认返回true, bloomFilterName:{},value:{}",bloomFilterName,value, e);
            return true;
        }
    }


    /**
     * @author chen
     * @description 初始化布隆过滤器
     * @date: 2024/2/27 8:16
     * @param filterEnum
     */
    private void initBloomFilter(BloomFilterEnum filterEnum) {
        try {
            RBloomFilter<Object> blf = getRedissonClient().getBloomFilter(filterEnum.getBloomFilterName());
            long expectedInsertions = filterEnum.getExpectedInsertions();
            double falseProbability = filterEnum.getFalseProbability();
            blf.tryInit(expectedInsertions, falseProbability);
            log.info("布隆过滤器初始化成功:{},预估已存在key数量count:{},expectedInsertions:{},falseProbability:{},size:{},hashIterations:{}",
                    filterEnum.getBloomFilterName(),blf.count(),blf.getExpectedInsertions(), blf.getFalseProbability(), blf.getSize(), blf.getHashIterations());
        } catch (Exception e) {
            log.error("布隆过滤器初始化失败,param={}", filterEnum, e);
        }
    }

    /**
     * 布隆过滤器配置
     */
    @Setter
    @Getter
    @ToString
    public static class BloomFilterConfig {
        /**
         * 布隆过滤名称
         */
        private String bloomFilterName;

        /**
         * 预计插入的记录数量
         */
        private Long expectedInsertions;

        /**
         * 容错率
         */
        private Double falseProbability;

        /**
         * 描述
         */
        private String desc;

        public BloomFilterConfig(String bloomFilterName, Long expectedInsertions, Double falseProbability, String desc) {
            this.bloomFilterName = bloomFilterName;
            this.expectedInsertions = expectedInsertions;
            this.falseProbability = falseProbability;
            this.desc = desc;
        }
    }

    /**
     * @author chen
     * @description 获取redisson客户端
     * @date: 2024/3/3 10:48
     * @return org.redisson.api.RedissonClient
     */
    public static RedissonClient getRedissonClient() {
        return RedissonInitConfiguration.getDefaultClient();
    }

}
